#!/bin/bash
# Author: fanyongfeng
# 安装rocketmq
# 操作系统: centos7/银河麒麟v10

set -e
# 使用root用户执行
[[ $(id -u) -ne 0 ]] && echo "请使用root用户执行此脚本" && exit 1

usage(){
    echo "Usage:"
    echo "sh $0
            --prefix=安装目录(默认/data/rocketmq-\$VERSION)
            --version=rocketmq版本(版本需>=5.0,默认安装5.2.0)
            --mode=single|cluster|controller
            --namesrv_ip_list=cluster或controller模式下需提供namesrv的ip列表
            --role=namesrv|namesrv-am|namesrv-bm|namesrv-ab|namesrv-ba|broker-am|broker-as|broker-bm|broker-bs|broker-ab|broker-ba
        "
    echo "示例:"
    echo "sh $0 --version=5.2.0 --mode=single"
    echo "sh $0 --version=5.2.0 --mode=cluster --namesrv_ip_list=192.168.100.101,192.168.100.102 --role=all-ab"
    echo "sh $0 --version=5.2.0 --mode=controller --namesrv_ip_list=192.168.100.101,192.168.100.102,192.168.100.103 --role=all-ab"
    echo "mode参数说明："
    echo "  1. single,在一台服务器上同时部署namesrv和broker"
    echo "  2. cluster,部署为2m-2s-async模式,集群模式至少需要2台服务器"
    echo "  3. controller,配置为主备自动切换的集群模式,需要3个节点部署controller组件(和namesrver部署在一起)"
    echo "namesrv_ip_list参数说明："
    echo "  1. cluster模式下,至少需要提供2台服务器部署namesrv"
    echo "                  namesrv_ip_list=ip1,ip2"
    echo "  2. controller模式下,需要3台服务器同时部署namesrv和controller"
    echo "                  namesrv_ip_list=ip1,ip2,ip3"
    echo "role参数说明："
    echo "  1. cluster模式下, role为namesrv时，仅部署namesrv"
    echo "                   role为namesrv-am时，部署namesrv、broker-a-master"
    echo "                   role为namesrv-bm时，部署namesrv、broker-b-master"
    echo "                   role为namesrv-ab时，部署namesrv、broker-a-master、broker-b-slave"
    echo "                   role为namesrv-ba时，部署namesrv、broker-b-master、broker-a-slave"
    echo "                   role为broker-ab时，同时部署broker-a-master、 broker-b-slave"
    echo "  2. controller模式下, role为namesrv时，部署namesrv、controller,其他同cluster模式"
    echo "  3. single模式下, 忽略role参数"
    exit 1
}

#处理参数，规范化参数
ARGS=$(getopt -o h --long prefix:,version:,mode:,namesrv_ip_list:,role:,help -n "$0" -- "$@")

if [[ $? != 0 || $# -eq 0 ]]
then
    usage
fi

#重新排列参数顺序
eval set -- "${ARGS}"
#通过shift和while循环处理参数
while true
do
    case $1 in
        --prefix)
            PREFIX=$2
            shift 2
            ;;
        --version)
            VERSION=$2
            shift 2
            ;;
        --mode)
            MODE=$2
            shift 2
            ;;
        --namesrv_ip_list)
            NAMESRV_IP_LIST=$2
            shift 2
            ;;
        --role)
            ROLE=$2
            shift 2
            ;;
        -h|--help)
            usage
            ;;
        --)
           shift
           break
           ;;
        *)
           echo "Internal error!"
           exit 1
           ;;
    esac
done

# 检查java环境
[[ $JAVA_HOME == "" ]] && echo "未找到环境变量\${JAVA_HOME},请先配置java环境." && exit 1

# 检查部署模式
if [[ ${MODE} == "cluster" || "${MODE}" == "controller" ]]
then
    [[ -z ${ROLE} ]] && echo "cluster或controller模式下需提供role参数" && exit 1
fi

NAMESRV_IP_LIST_COUNTS=$(echo "${NAMESRV_IP_LIST}" | sed 's/,/\n/g' | wc -l)
if [[ ${MODE} == "cluster" ]]
then
    if [[ ${NAMESRV_IP_LIST_COUNTS} -lt 2 ]]
    then
        echo "cluster模式至少需要提供2台服务器部署namesrv，请提供namesrv节点的ip列表"
        exit 1
    fi
    NAMESRV_ADDR=$(echo "${NAMESRV_IP_LIST}"|sed 's/,/:9876;/g'|sed 's/$/:9876/g')
elif [[ ${MODE} == "controller" ]]
then
    if [[ ${NAMESRV_IP_LIST_COUNTS} -ne 3 ]]
    then
        echo "controller模式需要提供3台服务器部署namesrv和controller，请提供namesrv节点的ip列表"
        exit 1
    fi
    NAMESRV_ADDR=$(echo "${NAMESRV_IP_LIST}"|sed 's/,/:9876;/g'|sed 's/$/:9876/g')
    controllerDLegerPeers=$(echo ${NAMESRV_IP_LIST} | sed 's/,/\n/g' | sort | sed 's/$/:9877/g' | xargs | sed 's/^/n1-/' | sed 's/ /;n2-/' | sed 's/ /;n3-/')
fi


VERSION=${VERSION:-5.2.0}
[[ "$(echo ${VERSION} | awk -F'.' '{print $1}')" -lt 5 ]] && echo "版本号必须>=5.0" && exit 1
MODE=${MODE:-single}

INSTALL_DIR="${PREFIX:-/data/rocketmq-${VERSION}}"
DATA_DIR="${INSTALL_DIR}/store"
RUN_USER="rocketmq"
HOST_IP=$(ip a | grep global | head -1 | awk -F'/' '{print $1}' | awk '{print $2}')

# 关闭防火墙和selinux
systemctl stop firewalld && systemctl disable firewalld
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce  0

# 下载安装包
[[ -f rocketmq-all-${VERSION}-bin-release.zip ]] || wget https://dist.apache.org/repos/dist/release/rocketmq/${VERSION}/rocketmq-all-${VERSION}-bin-release.zip
if [[ $? -ne 0 ]]
then
    echo "rocketmq-all-${VERSION}-bin-release.zip下载失败，请重试或手动上传此安装包"
    exit 1
fi

mkdir -p $(echo ${INSTALL_DIR%/*})
unzip rocketmq-all-${VERSION}-bin-release.zip && mv rocketmq-all-${VERSION}-bin-release ${INSTALL_DIR} 

# 添加运行账户并授权
id ${RUN_USER} || useradd ${RUN_USER} -s /sbin/nologin
chown -R ${RUN_USER}.${RUN_USER} ${INSTALL_DIR}

# controller模式下添加namesrv配置文件
if [[ ${MODE} == "controller" && $(echo ${ROLE} | grep 'namesrv') ]]
then
    controllerDLegerSelfId=$(echo ${controllerDLegerPeers} | sed 's/;/\n/g' | grep ${HOST_IP} | awk -F'-' '{print $1}')
cat > ${INSTALL_DIR}/conf/namesrv.conf <<EOF
#Namesrv config
listenPort = 9876
enableControllerInNamesrv = true

#controller config
controllerDLegerGroup = group1
controllerDLegerPeers = ${controllerDLegerPeers}
controllerDLegerSelfId = ${controllerDLegerSelfId}
controllerStorePath = ${INSTALL_DIR}/DledgerController
enableElectUncleanMaster = false
notifyBrokerRoleChanged = true
EOF
fi

# 添加broker配置文件
cat > ${INSTALL_DIR}/conf/broker.conf <<EOF
brokerClusterName = DefaultCluster
brokerName = broker-a
#0 表示 Master，>0 表示 Slave
brokerId = 0
#删除文件时间点，默认凌晨 4点
deleteWhen=04
#文件保留时间，默认48小时
fileReservedTime=48
#Broker 的角色，ASYNC_MASTER=异步复制Master，SYNC_MASTER=同步双写Master，SLAVE=slave节点
brokerRole=ASYNC_MASTER
#刷盘方式，ASYNC_FLUSH=异步刷盘，SYNC_FLUSH=同步刷盘 
flushDiskType=ASYNC_FLUSH
#nameServer地址，多台nameserver时用分号分割（即namesrvAddr=ip1:port1;ip2:port2;ip3:port3）
namesrvAddr = ${NAMESRV_ADDR:-${HOST_IP}:9876}
#Broker 对外服务的监听端口
listenPort=10911
brokerIP1 = ${HOST_IP}
#存储路径
storePathRootDir = ${INSTALL_DIR}/store
#每个topic对应队列的数量，默认为4，实际应参考consumer实例的数量，值过小不利于consumer负载均衡
defaultTopicQueueNums=8
#是否允许 Broker 自动创建Topic，生产建议关闭
autoCreateTopicEnable=true
#是否允许 Broker 自动创建订阅组，生产建议关闭
autoCreateSubscriptionGroup=true
#commitLog每个文件的大小默认1G
mapedFileSizeCommitLog=1073741824
#ConsumeQueue每个文件默认存30W条，根据业务情况调整
mapedFileSizeConsumeQueue = 300000
EOF

# 创建systemd服务文件
# 使用systemd管理namesrv
cat > /etc/systemd/system/rocketmq_namesrv.service << EOF
[Unit]
# 服务描述
Description=rocketmq_namesrv
Requires=network.service
After=network.target

[Service]
User=${RUN_USER}
Group=${RUN_USER}
LimitNOFILE=65536
Environment=JAVA_HOME=${JAVA_HOME}
ExecStart=${INSTALL_DIR}/bin/mqnamesrv -n "${HOST_IP}:9876"
ExecStop=${INSTALL_DIR}/bin/mqshutdown namesrv
Restart=on-failure
RestartSec=30s

[Install]
WantedBy=multi-user.target
EOF

# 使用systemd管理rocketmq_broker
cat > /etc/systemd/system/rocketmq_broker.service <<EOF
[Unit]
# 服务描述
Description=rocketmq_broker
Requires=network.service
After=network.target

[Service]
User=${RUN_USER}
Group=${RUN_USER}
LimitNOFILE=65536
Environment=JAVA_HOME=${JAVA_HOME}
ExecStart=${INSTALL_DIR}/bin/mqbroker -c ${INSTALL_DIR}/conf/broker.conf
ExecStop=${INSTALL_DIR}/bin/mqshutdown broker
KillMode=none
Restart=on-failure
RestartSec=30s

[Install]
WantedBy=multi-user.target
EOF

# 设置namesrv 和 broker 的JVM
# nameserver : broker JVM内存比 1 ：2
# 5.1.2版本中runbroker.runserver.sh的默认JVM设置
# runbroker.sh:JAVA_OPT="${JAVA_OPT} -server -Xms8g -Xmx8g"
# runserver.sh:      JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -Xmn2g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"
# runserver.sh:      JAVA_OPT="${JAVA_OPT} -server -Xms4g -Xmx4g -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=320m"

# 使用available内存的70%分配JVM内存
free_memory=$(free -g | sed -n '2p'| awk '{print int($7 * 0.7)}')
# 服务器上同时运行namesrv,broker-master,broker-slave时，内存分配比例为1:2:2
if [[ $(echo "${ROLE}" | grep 'ab' ) ]]
then
    memory_size=$((${free_memory} / 5))
else
    memory_size=$((${free_memory} / 3))
fi

if [[ ${memory_size} -eq 0 ]]
then
    nameserver_mem=256m
    nameserver_mem_xmn=256m
    broker_mem=512m
else
    nameserver_mem=${memory_size}g
    broker_mem=$((${memory_size} * 2))g
    if [[ ${memory_size} -lt 2 ]]
    then
        nameserver_mem_xmn=256m
    else
        nameserver_mem_xmn=$((${memory_size} / 2))g
    fi
fi

# 设置namesrv JVM
sed -i "s/-server -Xms4g -Xmx4g -Xmn2g/-server -Xms${nameserver_mem} -Xmx${nameserver_mem} -Xmn${nameserver_mem_xmn}/"  ${INSTALL_DIR}/bin/runserver.sh
sed -i "s/-server -Xms4g -Xmx4g -XX/-server -Xms${nameserver_mem} -Xmx${nameserver_mem} -XX/"  ${INSTALL_DIR}/bin/runserver.sh
# 设置broker JVM
sed -i "s/-server -Xms8g -Xmx8g/-server -Xms${broker_mem} -Xmx${broker_mem}/"  ${INSTALL_DIR}/bin/runbroker.sh

# 更改namesrv和broker的日志目录
sed -i "/^choose_gc_options$/a\JAVA_OPT=\"\${JAVA_OPT} -Duser.home=${INSTALL_DIR}\"" ${INSTALL_DIR}/bin/runserver.sh
sed -i "/^choose_gc_options$/a\JAVA_OPT=\"\${JAVA_OPT} -Duser.home=${INSTALL_DIR}\"" ${INSTALL_DIR}/bin/runbroker.sh
# 注释runbroker.sh和runserver.sh的find_java_home，解决未找到JAVA_HOME的错误
sed -i 's/^find_java_home$/#find_java_home/g' ${INSTALL_DIR}/bin/runbroker.sh ${INSTALL_DIR}/bin/runserver.sh

# 修改broker的master、slave配置文件
if [[ ${ROLE} == "namesrv-am" || ${ROLE} == "broker-am" ]]
then
    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-a.properties

    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker.service
    sed -i 's#broker.conf#2m-2s-async/broker-a.properties#g' /etc/systemd/system/rocketmq_broker.service
elif [[ ${ROLE} == "namesrv-bm" || ${ROLE} == "broker-bm" ]]
then
    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties
    sed -i "s#^brokerName.*#brokerName=broker-b#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties

    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker.service
    sed -i 's#broker.conf#2m-2s-async/broker-b.properties#g' /etc/systemd/system/rocketmq_broker.service
elif [[ ${ROLE} == "namesrv-ab" || ${ROLE} == "broker-ab" ]]
then
    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-a.properties
    sed -i "s#^storePathRootDir.*#storePathRootDir=${INSTALL_DIR}/store-a#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-a.properties

    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i "s#^brokerName.*#brokerName=broker-b#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i 's/^brokerId.*/brokerId = 1/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i 's/^brokerRole.*/brokerRole=SLAVE/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i "s#^storePathRootDir.*#storePathRootDir=${INSTALL_DIR}/store-b#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i "s#^listenPort.*#listenPort=11911#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties

    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker-a.service
    sed -i 's#broker.conf#2m-2s-async/broker-a.properties#g' /etc/systemd/system/rocketmq_broker-a.service
    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker-b.service
    sed -i 's#broker.conf#2m-2s-async/broker-b-s.properties#g' /etc/systemd/system/rocketmq_broker-b.service
elif [[ ${ROLE} == "namesrv-ba" || ${ROLE} == "broker-ba" ]]
then
    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties
    sed -i "s#^brokerName.*#brokerName=broker-b#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties
    sed -i "s#^storePathRootDir.*#storePathRootDir=${INSTALL_DIR}/store-b#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties
    sed -i "s#^listenPort.*#listenPort=11911#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties

    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties
    sed -i 's/^brokerId.*/brokerId = 1/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties
    sed -i 's/^brokerRole.*/brokerRole=SLAVE/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties
    sed -i "s#^storePathRootDir.*#storePathRootDir=${INSTALL_DIR}/store-a#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties

    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker-a.service
    sed -i 's#broker.conf#2m-2s-async/broker-a-s.properties#g' /etc/systemd/system/rocketmq_broker-a.service
    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker-b.service
    sed -i 's#broker.conf#2m-2s-async/broker-b.properties#g' /etc/systemd/system/rocketmq_broker-b.service
elif [[ ${ROLE} == "broker-as" ]]
then
    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties
    sed -i 's/^brokerId.*/brokerId = 1/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties
    sed -i 's/^brokerRole.*/brokerRole=SLAVE/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-a-s.properties

    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker.service
    sed -i 's#broker.conf#2m-2s-async/broker-a-s.properties#g' /etc/systemd/system/rocketmq_broker.service
elif [[ ${ROLE} == "broker-bs" ]]
then
    \cp ${INSTALL_DIR}/conf/broker.conf ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i "s#^brokerName.*#brokerName=broker-b#g" ${INSTALL_DIR}/conf/2m-2s-async/broker-b.properties
    sed -i 's/^brokerId.*/brokerId = 1/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties
    sed -i 's/^brokerRole.*/brokerRole=SLAVE/g' ${INSTALL_DIR}/conf/2m-2s-async/broker-b-s.properties

    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker.service
    sed -i 's#broker.conf#2m-2s-async/broker-b-s.properties#g' /etc/systemd/system/rocketmq_broker.service
fi

echo "安装完成，正在启动服务..."
# 修改namesrv.service并启动
if [[ $(echo ${ROLE} | grep 'namesrv') ]]
then
    \cp ${INSTALL_DIR}/conf/rocketmq_namesrv.service /etc/systemd/system/rocketmq_namesrv.service
    if [[ ${MODE} == "controller" ]]
    then
        sed -i "/^ExecStart/s/$/ -c namesrv.conf/" /etc/systemd/system/rocketmq_namesrv.service
        sed -i "s#namesrv.conf#${INSTALL_DIR}/conf/namesrv.conf#" /etc/systemd/system/rocketmq_namesrv.service
    fi
    systemctl enable rocketmq_namesrv
    systemctl start rocketmq_namesrv
    sleep 2
    systemctl status rocketmq_namesrv
fi

systemctl daemon-reload
if [[ ${MODE} == "single" ]]
then
    \cp ${INSTALL_DIR}/conf/rocketmq_broker.service /etc/systemd/system/rocketmq_broker.service
    \cp ${INSTALL_DIR}/conf/rocketmq_namesrv.service /etc/systemd/system/rocketmq_namesrv.service
    systemctl daemon-reload
    systemctl enable rocketmq_namesrv
    systemctl enable rocketmq_broker
    systemctl start rocketmq_namesrv
    systemctl status rocketmq_namesrv
    sleep 5
    systemctl start rocketmq_broker
    systemctl status rocketmq_broker
else
    for rocketmq_service in $(ls /etc/systemd/system/ | grep -v 'rocketmq_namesrv' | grep 'rocketmq_')
    do
        systemctl enable ${rocketmq_service}
        systemctl start ${rocketmq_service}
        sleep 2
        systemctl status ${rocketmq_service}
    done
fi